﻿namespace TomatoBreak.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Web.Mvc;

    public class FilterRegistry
    {
        public FilterRegistry(IDependencyResolver resolver)
        {
            if (resolver == null)
            {
                throw new ArgumentNullException("resolver");
            }

            ControllerRegistry = new List<FilterRegistryItem>();
            ActionRegistry = new List<FilterRegistryItem>();
            Resolver = resolver;
        }

        protected internal virtual ICollection<FilterRegistryItem> ControllerRegistry
        {
            get; private set;
        }

        protected internal virtual ICollection<FilterRegistryItem> ActionRegistry
        {
            get; private set;
        }

        protected internal IDependencyResolver Resolver { get; private set; }

        public virtual FilterRegistry Register<TController, TFilter>(IEnumerable<Func<TFilter>> filters)
            where TController : Controller
            where TFilter : IMvcFilter
        {
            if (filters == null)
            {
                throw new ArgumentNullException("filters");
            }

            ControllerRegistry.Add(new FilterRegistryControllerItem<TController>(ConvertFilters(filters)));

            return this;
        }

        public virtual FilterRegistry Register<TController, TFilter>(Expression<Action<TController>> action, IEnumerable<Func<TFilter>> filters)
            where TController : Controller
            where TFilter : IMvcFilter
        {
            if (filters == null)
            {
                throw new ArgumentNullException("filters");
            }

            ActionRegistry.Add(new FilterRegistryActionItem<TController>(action, ConvertFilters(filters)));

            return this;
        }

        public virtual IEnumerable<FilterRegistryItem> FindControllerFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return ControllerRegistry.Where(i => i.IsMatching(controllerContext, actionDescriptor));
        }

        public virtual IEnumerable<FilterRegistryItem> FindActionFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            return ActionRegistry.Where(i => i.IsMatching(controllerContext, actionDescriptor));
        }

        private static IEnumerable<Func<IMvcFilter>> ConvertFilters<TFilter>(IEnumerable<Func<TFilter>> filters) where TFilter : IMvcFilter
        {
            return filters.Select(filter => new Func<IMvcFilter>(() => filter() as IMvcFilter));
        }
    }
}